/*
 *  TexturePacker Importer
 *  (c) CodeAndWeb GmbH, Saalbaustraße 61, 89233 Neu-Ulm, Germany
 * 
 *  Use this script to import sprite sheets generated with TexturePacker.
 *  For more information see http://www.codeandweb.com/texturepacker/unity
 * 
 *  Thanks to Brendan Campbell for providing a first version of this script!
 *
 */

using UnityEditor;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Xml;
using System.Text.RegularExpressions;
using System.Reflection;


public class DataSheetInfo
{
	public string name;
	public Rect rect;
	public Vector2 pivot;
}



public class TexturePackerImporter : AssetPostprocessor
{
    const string IMPORTER_VERSION = "3.5.0";

    static bool isEnable = false;

    static string[] textureExtensions = {
		".png",
		".jpg",
		".jpeg",
		".tiff",
		".tga",
		".bmp"
	};

    public static bool isTexturePath(string path)
    {
        string ext = System.IO.Path.GetExtension(path).ToLower();
        return isTextureExtension(ext);
    }
    public static bool isTextureExtension(string ext)
    {
        return System.Array.FindIndex(textureExtensions, (a) =>
            {
                return a == ext;
            }) >= 0;
    }

    static bool importPivotPoints = EditorPrefs.GetBool("TPImporter.ImportPivotPoints", true);

    /*
     *  Pivot point import can be disabled in the Preferences dialog (menu item Unity->Preferences, TexturePacker sheet)
     */
    [PreferenceItem("TexturePacker")]
    static void PreferencesGUI()
    {
        importPivotPoints = EditorGUILayout.Toggle("Always import pivot points", importPivotPoints);
        if (GUI.changed)
            EditorPrefs.SetBool("TPImporter.ImportPivotPoints", importPivotPoints);
    }


    /*
     *  Trigger a texture file re-import each time the .tpsheet file changes (or is manually re-imported)
     */
    static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
		if(!isEnable)
		{
			return;
		}

        foreach (var asset in importedAssets)
        {
            if (!Path.GetExtension(asset).Equals(".tpsheet"))
                continue;
            foreach (string ext in textureExtensions)
            {
                string pathToTexture = Path.ChangeExtension(asset, ext);
                if (File.Exists(pathToTexture))
                {
                    AssetDatabase.ImportAsset(pathToTexture, ImportAssetOptions.ForceUpdate);
                    break;
                }
            }
        }
    }

    [MenuItem("Assets/Custom/OldProject/Sprite/BuildSpriteSheet")]
	public static void BuildSpriteSheet()
	{
		Object[] selectedAsset = Selection.GetFiltered (typeof(Texture2D), SelectionMode.DeepAssets);
		for(int i = 0; i < selectedAsset.Length; i++)
		{
			BuildSpriteSheet(selectedAsset[i]);
		}
	}

	public static void BuildSpriteSheet(Object obj)
	{
		string path = AssetDatabase.GetAssetPath(obj);
		string pathToData = Path.ChangeExtension(path, ".tpsheet");
		if (File.Exists(pathToData))
		{
			TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
			updateSpriteMetaDataByPtsheet(textureImporter, pathToData, 1);
		}
	}

    /*
     *  Trigger a sprite sheet update each time the texture file changes (or is manually re-imported)
     */
    void OnPreprocessTexture()
    {
        if(!isEnable)
        {
            return;
        }

//        TextureImporter importer = assetImporter as TextureImporter;
//        {
//            string pathToData = Path.ChangeExtension(assetPath, ".tpsheet");
//            if (File.Exists(pathToData))
//            {
//                updateSpriteMetaDataByPtsheet(importer, pathToData);
//            }
//        }
//
//        {
//            string pathToData = Path.ChangeExtension(assetPath, ".plist");
//            if (File.Exists(pathToData))
//            {
//                updateSpriteMetaDataByPlist(importer, pathToData);
//            }
//        }
//        {
//            string pathToData = Path.ChangeExtension(assetPath, ".atlas");
//            if (File.Exists(pathToData))
//            {
//                updateSpriteMetaDataUsingAtlas(importer, pathToData);
//            }
//        }

    }

	static void addSpriteMetas (SpriteMetaData smd, string realSpriteName, List<SpriteMetaData> metaDatas, float scale)
	{
		Object[] objs = AssetDatabase.LoadAllAssetsAtPath (realSpriteName);
		List<Sprite> sprites = new List<Sprite> ();
		for (int t = 0; t < objs.Length; t++) {
			if (objs [t] is Sprite) {
				sprites.Add (objs [t] as Sprite);
			}
		}
		for (int t = 0; t < sprites.Count; t++) {
			SpriteMetaData spriteSmd = smd;
			Sprite oldSprite = sprites [t];
			Texture2D tex = oldSprite.texture;
			if (sprites.Count > 1) {
				spriteSmd.name += ResourcesEx.dirSymbol + oldSprite.name;
			}

			Vector4 padding = MaterialTextureForETC1.getSpritePadding (spriteSmd.pivot, spriteSmd.rect, scale, new Rect (0, 0, tex.width, tex.height));
			Vector4 spritePadding = new Vector4 (oldSprite.rect.xMin, oldSprite.rect.yMin, tex.width - oldSprite.rect.xMax, tex.height - oldSprite.rect.yMax);
			spritePadding *= scale;
			Vector4 spritePadding2 = spritePadding;
			for (int i = 0; i < 4; i++) {
				spritePadding2 [i] -= padding [i];
				spritePadding2 [i] = Mathf.Max (0, spritePadding2 [i]);
			}
			spriteSmd.rect.x += spritePadding2.x;
			spriteSmd.rect.y += spritePadding2.y;
			spriteSmd.rect.width -= spritePadding2.x + spritePadding2.z;
			spriteSmd.rect.height -= spritePadding2.y + spritePadding2.w;
			Vector4 innerPadding = spritePadding2 + padding;
			for (int i = 0; i < 4; i++) {
				innerPadding [i] -= spritePadding [i];
			}
			spriteSmd.pivot = new Vector2 (0.5f + (innerPadding.z - innerPadding.x) / (2 * spriteSmd.rect.width), 0.5f + (innerPadding.w - innerPadding.y) / (2 * spriteSmd.rect.height));
			Vector4 border = oldSprite.border * scale;

//            if (oldSprite.name == "rw_07_1")
//            {
//                Debug.LogError("test_sprite: " + oldSprite.name + "   " + border + "   " + spritePadding2 + "   " + padding);
//            }

			for (int i = 0; i < 4; i++) {
				border [i] -= spritePadding2 [i] + padding [i];
				border [i] = Mathf.Max (0, border [i]);
                border [i] = Mathf.Min ((i % 2 == 0)? spriteSmd.rect.width: spriteSmd.rect.height, border [i]);
			}

//            if (oldSprite.name == "rw_07_1")
//            {
//                Debug.LogError("test_sprite_2: " + oldSprite.name + "   " + border + "   " + spritePadding2 + "   " + padding);
//            }

			spriteSmd.border = border;
			Vector2 oldPivot = oldSprite.GetPivot ();
			Vector2 offset = new Vector2 ((oldPivot.x - 0.5f) * oldSprite.rect.width, (oldPivot.y - 0.5f) * oldSprite.rect.height);
			spriteSmd.pivot.x += offset.x / spriteSmd.rect.width;
			spriteSmd.pivot.y += offset.y / spriteSmd.rect.height;

			spriteSmd.alignment = (int)UnityEngine.SpriteAlignment.Custom;
			metaDatas.Add (spriteSmd);
		}
	}

	public static void updateSpriteMetaData(TextureImporter importer, string pathToData, string srcPath, string ext, float scale)
	{
		string realDataPath = pathToData + ".tpsheet";
		if(File.Exists(realDataPath))
		{
			updateSpriteMetaDataByPtsheet(importer, realDataPath, scale, srcPath, ext);
		}
		else
		{
			realDataPath = pathToData + ".plist";
			if(File.Exists(realDataPath))
			{
				updateSpriteMetaDataByPlist(importer, realDataPath, scale, srcPath, ext);
			}
		}

		CommonUtil.Delete(realDataPath);
	}

    private static void updateSpriteMetaDataByPtsheet(TextureImporter importer, string pathToData, float scale, string srcPath = null, string ext = null)
    {
		if(importer == null)
		{
			Debug.Log("importer is null");
			return;
		}

        if (importer.textureType != TextureImporterType.Default)
        {
            importer.textureType = TextureImporterType.Sprite;
        }

		importer.setEvonyAttributes();
        importer.spriteImportMode = SpriteImportMode.Multiple;
        string[] dataFileContent = File.ReadAllLines(pathToData);
        int format = 30302;

        foreach (string row in dataFileContent)
        {
            if (row.StartsWith(":format="))
            {
                format = int.Parse(row.Remove(0, 8));
            }
        }
//        if (format != 30302)
//        {
//            EditorUtility.DisplayDialog("Please update TexturePacker Importer", "Your TexturePacker Importer is too old, \nplease load a new version from the asset store!", "Ok");
//            return;
//        }

//        Dictionary<string, SpriteMetaData> existingSprites = new Dictionary<string, SpriteMetaData>();
//        foreach (SpriteMetaData sprite in importer.spritesheet)
//        {
//            existingSprites.Add(sprite.name, sprite);
//        }

        List<SpriteMetaData> metaData = new List<SpriteMetaData>();
        foreach (string row in dataFileContent)
        {
            if (string.IsNullOrEmpty(row) || row.StartsWith("#") || row.StartsWith(":"))
                continue; // comment lines start with #, additional atlas properties with :

            string[] cols = row.Split(';');
            if (cols.Length < 7)
                return; // format error

			if(cols.Length > 7)
			{
				for(int i = 0; i < cols.Length - 7; i++)
				{
					cols[0] += cols[i + 1];
				}
				for(int i = 1; i < 7; i++)
				{
					cols[i] = cols[cols.Length - 7 + i];
				}
			}

            SpriteMetaData smd = new SpriteMetaData();
			try
			{
				smd.name = cols[0].Replace('/', ResourcesEx.dirSymbol);  // unity has problems with "/" in sprite names...
	            float x = float.Parse(cols[1]);
	            float y = float.Parse(cols[2]);
	            float w = float.Parse(cols[3]);
	            float h = float.Parse(cols[4]);
	            float px = float.Parse(cols[5]);
	            float py = float.Parse(cols[6]);
				smd.rect = new UnityEngine.Rect(x, y, w, h);

				string realSpriteName = srcPath + "/" + cols[0] + ext;

				smd.pivot = new UnityEngine.Vector2(px, py);

				addSpriteMetas (smd, realSpriteName, metaData, scale);
			}
			catch(System.FormatException e)
			{
				Debug.LogException(e);
				return;
			}
        }

        importer.spritesheet = metaData.ToArray();
    }

	public static Dictionary<string, DataSheetInfo> ParseDataFile(string pathToData)
	{
		string[] dataFileContent = File.ReadAllLines(pathToData);
		int format = 30302;
		
		foreach (string row in dataFileContent)
		{
			if (row.StartsWith(":format="))
			{
				format = int.Parse(row.Remove(0, 8));
			}
		}
//		if (format != 30302)
//		{
//			EditorUtility.DisplayDialog("Please update TexturePacker Importer", "Your TexturePacker Importer is too old, \nplease load a new version from the asset store!", "Ok");
//			return null;
//		}

		Dictionary<string, DataSheetInfo> dataSheetInfo = new Dictionary<string, DataSheetInfo>();
		List<SpriteMetaData> metaData = new List<SpriteMetaData>();
		foreach (string row in dataFileContent)
		{
			if (string.IsNullOrEmpty(row) || row.StartsWith("#") || row.StartsWith(":"))
				continue; // comment lines start with #, additional atlas properties with :
			
			string[] cols = row.Split(';');
			if (cols.Length < 7)
				return null; // format error
			
//			if(cols.Length > 7)
//			{
//				for(int i = 0; i < cols.Length - 7; i++)
//				{
//					cols[0] += cols[i + 1];
//				}
//				for(int i = 1; i < 7; i++)
//				{
//					cols[i] = cols[cols.Length - 7 + i];
//				}
//			}

			try
			{
				DataSheetInfo info = new DataSheetInfo();
				info.name = cols[0];  // unity has problems with "/" in sprite names...
				info.rect.x = float.Parse(cols[1]);
				info.rect.y = float.Parse(cols[2]);
				info.rect.width = float.Parse(cols[3]);
				info.rect.height = float.Parse(cols[4]);
				info.pivot.x = float.Parse(cols[5]);
				info.pivot.y = float.Parse(cols[6]);
				dataSheetInfo.Add(info.name, info);
			}
			catch(System.FormatException e)
			{
				Debug.LogException(e);
				return null;
			}
		}

		return dataSheetInfo;
	}

	private static void updateSpriteMetaDataByPlist(TextureImporter importer, string pathToData, float scale, string srcPath = null, string ext = null)
    {
        object[] args = new object[2] { 0, 0 };
        MethodInfo mi = typeof(TextureImporter).GetMethod("GetWidthAndHeight", BindingFlags.NonPublic | BindingFlags.Instance);
        mi.Invoke(importer, args);

        int texWidth = (int)args[0];
        int texHeight = (int)args[1];

        if (importer.textureType != TextureImporterType.Default)
        {
            importer.textureType = TextureImporterType.Sprite;
        }
		importer.setEvonyAttributes();
        importer.spriteImportMode = SpriteImportMode.Multiple;

//        Dictionary<string, SpriteMetaData> existingSprites = new Dictionary<string, SpriteMetaData>();
//        foreach (SpriteMetaData sprite in importer.spritesheet)
//        {
//            existingSprites.Add(sprite.name, sprite);
//        }

        List<SpriteMetaData> metaData = new List<SpriteMetaData>();

        StreamReader sr = new StreamReader(pathToData);
        string t_OrginalPlist = sr. ReadToEnd();
        sr.Close();

        //
        XmlDocument xml = new XmlDocument();
        xml.LoadXml(t_OrginalPlist);

        // Find the root plist object. If it doesn't exist or isn't a plist node, state so and return null.
        XmlNode plistNode = xml. LastChild;
        if (plistNode.Name != "plist") { Debug.LogError("This is not a plist file: " + " File name"); return; }

        // Get the version number of this plist file. This script was designed to work with version 1.0. If this is an incorrect version, state so and return null
        string plistVers = plistNode.Attributes["version"].InnerText;
        string plistVersSupported = "1.0";
        if (plistVers != plistVersSupported) { Debug.LogError("This is an unsupported plist version: " + plistVers + ". Require version " + plistVersSupported); return; }

        // Get the root plist dict object. This is the root object that contains all the data in the plist file. This object will be the hashtable.
        XmlNode dictNode = plistNode.FirstChild;
        if (dictNode.Name != "dict") { Debug.LogError("Missing root dict from plist file: " + "File name"); return; }

        XmlNodeList nodeList = dictNode.ChildNodes[1].ChildNodes;
        string frameName = null;
        foreach (XmlNode node in nodeList)
        {
            if (frameName == null)
            {
                frameName = node.InnerText;
            }
            else
            {
                XmlNodeList frameAttrs = node.ChildNodes;

                
                Rect frameRect = new Rect();
                Vector2 offset2 = Vector2.zero;
                Rect sourceColorRect = new Rect();
                Vector2 sourceSize = Vector2.zero;

                string attrName = null;
                foreach(XmlNode frameAttr in frameAttrs)
                {
                    if (attrName == null)
                    {
                        attrName = frameAttr.InnerText;
                    }
                    else
                    {
						if(attrName == "textureRect")
                        {
                            if(!parseRect(frameAttr.InnerText, out frameRect))
                            {
                                continue;
                            }
                        }
						else if(attrName == "spriteOffset")
                        {
							if (!parseVector2(frameAttr.InnerText, out offset2))
                            {
                                continue;
                            }
                        }
						else if(attrName == "textureRotated")
                        {
                            if (frameAttr.Name == "true")
                            {
                                float temp = frameRect.width;
                                frameRect.width = frameRect.height;
                                frameRect.height = temp;
                            }
                        }
//                        else if(attrName == "sourceColorRect")
//                        {
//                            if (!parseRect(frameAttr.InnerText, out sourceColorRect))
//                            {
//                                continue;
//                            }
//                        }
						else if(attrName == "spriteSourceSize")
                        {
                            if (!parseVector2(frameAttr.InnerText, out sourceSize))
                            {
                                continue;
                            }
                        }
                           

                        attrName = null;
                    }
                }

                frameRect.y = texHeight - frameRect.y - frameRect.height;
				string ext2 = Path.GetExtension(frameName);
				frameName = frameName.Replace(ext2, "");

                SpriteMetaData smd = new SpriteMetaData();
				smd.name = frameName.Replace('/', ResourcesEx.dirSymbol);
                smd.rect = frameRect;
//				float x = smd.rect.x;
//				float y = smd.rect.y;
				float w = smd.rect.width;
				float h = smd.rect.height;


				string realSpriteName = srcPath + "/" + frameName + ext;

				Texture2D tex = AssetDatabase.LoadAssetAtPath(realSpriteName, typeof(Texture2D)) as Texture2D;

				offset2.y = tex.height - sourceSize.y - Mathf.Abs(offset2.y);
				smd.pivot.x = (0.5f * tex.width - offset2.x) / w;
				smd.pivot.y = (0.5f * tex.height - offset2.y) / h;

				addSpriteMetas(smd, realSpriteName, metaData, scale);

//				Sprite oldSprite = AssetDatabase.LoadAssetAtPath(realSpriteName, typeof(Sprite)) as Sprite;
//				if(oldSprite != null)
//				{
//					Vector4 padding = MaterialTextureForETC1.getSpritePadding(smd.pivot, smd.rect, oldSprite.rect);
//					Vector4 border = oldSprite.border;
//					for(int i = 0; i < 4; i++)
//					{
//						border[i] -= padding[i];
//						border[i] = Mathf.Max(0, border[i]);
//					}
//					smd.border = border;
//
//
//
//					Vector2 oldPivot = oldSprite.GetPivot();
//					Vector2 offset = new Vector2((oldPivot.x - 0.5f) * oldSprite.rect.width, (oldPivot.y - 0.5f) * oldSprite.rect.height);
//					smd.pivot.x += offset.x / w;
//					smd.pivot.y += offset.y / h;
//
//					smd.alignment = (int)UnityEngine.SpriteAlignment.Custom;
//				}
//				
//				metaData.Add(smd);

                frameName = null;
            }
        }

        importer.spritesheet = metaData.ToArray();
    }

    public void updateSpriteMetaDataUsingAtlas(TextureImporter importer, string pathToData)
    {
        object[] args = new object[2] { 0, 0 };
        MethodInfo mi = typeof(TextureImporter).GetMethod("GetWidthAndHeight", BindingFlags.NonPublic | BindingFlags.Instance);
        mi.Invoke(importer, args);

        int texWidth = (int)args[0];
        int texHeight = (int)args[1];

        if (importer.textureType != TextureImporterType.Default)
        {
            importer.textureType = TextureImporterType.Sprite;
        }
        importer.spriteImportMode = SpriteImportMode.Multiple;

//        Dictionary<string, SpriteMetaData> existingSprites = new Dictionary<string, SpriteMetaData>();
//        foreach (SpriteMetaData sprite in importer.spritesheet)
//        {
//            existingSprites.Add(sprite.name, sprite);
//        }

        List<SpriteMetaData> metaData = new List<SpriteMetaData>();
        

        string[] dataFileContent = File.ReadAllLines(pathToData);

        int i = 0;
        string frameName = null;
        Vector2 xy = Vector2.zero;
        Vector2 size = Vector2.zero;

        
        Vector2 offset = Vector2.zero;
        //Rect sourceColorRect = new Rect();
        //Vector2 sourceSize = Vector2.zero;
        bool rotate = false;
        foreach (string row in dataFileContent)
        {
            if (i < 4)
            {
                i++;
                continue;
            }

            int index = (i - 4) % 7;
            switch(index)
            {
                case 0:
                {
                    frameName = row;
                    break;
                }
                
                default:
                {
                    if (row.Contains("rotate:"))
                    {
                        if (row.Contains("true"))
                        {
                            rotate = true;
                        }
                    }
                    else if(row.Contains("xy:"))
                    {
                        parseVector2(row, out xy);
                    }
                    else if (row.Contains("size:"))
                    {
                        parseVector2(row, out size);
                    }
                    else if (row.Contains("orig:"))
                    {

                    }
                    else if (row.Contains("offset:"))
                    {
                        parseVector2(row, out offset);
                    }
                    else if (row.Contains("index:"))
                    {
                        
                    }
                }
                break;
            }

            if (index == 6)
            {
                if (frameName.Contains("ActivitiesCoin_1_s"))
                {
                    int j = 0;
                    j++;
                }

                //Rect frameRect = new Rect(xy.x + 1, xy.y + 1, size.x, size.y);
                Rect frameRect = new Rect(xy.x, xy.y, size.x, size.y);
                if (rotate)
                {
                    float temp = frameRect.width;
                    frameRect.width = frameRect.height;
                    frameRect.height = temp;
                }

                frameRect.y = texHeight - frameRect.y - frameRect.height;
                //if (rotate)
                //{
                //    frameRect.x += offset.x;
                //    frameRect.y -= offset.y;
                //}
                //else
                //{
                //    frameRect.x += offset.y;
                //    frameRect.y -= offset.x;
                //}
                SpriteMetaData smd = new SpriteMetaData();
                smd.name = frameName;
                smd.rect = frameRect;

//                if (existingSprites.ContainsKey(smd.name))
//                {
//                    SpriteMetaData sprite = existingSprites[smd.name];
//                    smd.pivot = sprite.pivot;
//                    smd.alignment = sprite.alignment;
//                    smd.border = sprite.border;
//                }

                metaData.Add(smd);

                frameName = null;
                rotate = false;
            }
            i++;
        }

        importer.spritesheet = metaData.ToArray();
    }

    public static bool parseRect(string rectStr, out Rect rect)
    {
        MatchCollection collection = Regex.Matches(rectStr, "[0-9]+", RegexOptions.None);
        if (collection.Count < 4)
        {
            Debug.LogError("parse rect failed.");
            rect = new Rect();
            return false;
        }

        int x = 0;
        int y = 0;
        int w = 0;
        int h = 0;
        int i = 0;
        foreach (Match match in collection)
        {
            switch (i)
            {
                case 0:
                {
                    string value = match.Groups[0].Value;
                    x = int.Parse(value);
                }
                break;

                case 1:
                {
                    string value = match.Groups[0].Value;
                    y = int.Parse(value);
                }
                break;

                case 2:
                {
                    string value = match.Groups[0].Value;
                    w = int.Parse(value);
                }
                break;

                case 3:
                {
                    string value = match.Groups[0].Value;
                    h = int.Parse(value);
                }
                break;
            }

            i++;
        }

        rect = new Rect(x, y, w, h);

        return true;
    }

	public static bool parseVector2(string rectStr, out Vector2 v)
    {
        MatchCollection collection = Regex.Matches(rectStr, "-?[0-9]+", RegexOptions.None);
        if (collection.Count < 2)
        {
            Debug.LogError("parse vector2 failed.");
            v = Vector2.zero;
            return false;
        }

        int x = 0;
        int y = 0;
        int i = 0;
        foreach (Match match in collection)
        {
            switch (i)
            {
                case 0:
                {
                    string value = match.Groups[0].Value;
                    x = int.Parse(value);
                }
                break;

                case 1:
                {
                    string value = match.Groups[0].Value;
                    y = int.Parse(value);
                }
                break;
            }

            i++;
        }

        v.x = x;
        v.y = y;

        return true;
    }
}